package com.cloud.idempotent;


import com.cloud.idempotent.annotation.IdempotentAnnotation;
import com.cloud.idempotent.exception.IdempotentException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class IdempotentIntercepter {

    @Autowired
    private RedisTemplate redisTemplate;

    private final static String annotationEL = "@annotation(com.demo.idempotent.annotation.IdempotentAnnotation)";

    @Pointcut(annotationEL)
    public void idempotentFunction() {
    }

    /**
     * 环绕增强，相当于MethodInterceptor
     */
    @Around("idempotentFunction()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        IdempotentAnnotation idempotentAnnotation =  method.getAnnotation(IdempotentAnnotation.class);

        // 获取注解参数
        String idempotentKey = idempotentAnnotation.idempotentKey();
        long timeout = idempotentAnnotation.timeout();

        //解析EL表达式
        String result = getValue(idempotentKey,joinPoint.getArgs(),method);
        log.info("idempotent key parse:{}",result);
        //判断是否重复提交
        if (redisTemplate.hasKey(result)) {
            throw new IdempotentException("该接口不能重复提交");
        }
        redisTemplate.opsForValue().set(result, result, timeout, TimeUnit.MILLISECONDS);
        //执行方法
        Object object = joinPoint.proceed();
        return object;
    }

    public String getValue(String idempotentKey, Object[] args,Method method) {
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(idempotentKey);
        EvaluationContext context = new StandardEvaluationContext();
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        String[] paramNames = discoverer.getParameterNames(method);
        for(int i = 0 ; i < args.length ; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return expression.getValue(context).toString();
    }
}
